#include "tcp.h"
int tcpinit(u_int16_t port,const char* ip){
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=htons(port);
    serveraddr.sin_addr.s_addr=inet_addr(ip);
    int ret = bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    ERROR_CHECK(ret,-1,"bind");
    return listenfd;
}
void epollinit(int epfd,int fd){
    struct epoll_event evt;
    evt.data.fd=fd;
    evt.events=EPOLLIN;
    int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&evt);
    ERROR_CHECK(ret,-1,"epoll_ctl");
}
void channelsinit(channel_t channels[],int max){
    for(int i = 0;i<max;i++){
        channels[i].fd=-1;
        channels[i].recvbufsize=0;
        memset(channels[i].recvbuf,0,BUFSIZE);
        (&channels[i])->sendbufsize=0;
        memset(channels[i].sendbuf,0,BUFSIZE);
    }
}
void addepollread(int epfd,int fd){
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;
    epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&evt);
}

void addepollwrite(int epfd,int fd){
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN|EPOLLOUT;
    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&evt);
}
void delepollwrite(int epfd,int fd){
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;
    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&evt);
}
void addchannel(channel_t channels[],int max,int fd){
    for(int i = 0;i<max;i++){
        if(channels[i].fd==-1){
           channels[i].fd=fd; 
           break;
        }
    }
}
int addaccept(int fd){
    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));
    socklen_t len = sizeof(clientaddr);
    int peerfd = accept(fd,(struct sockaddr*)&clientaddr,&len);
    printf("accept a new connect, ip:%s,port:%d\n",
                                        inet_ntoa(clientaddr.sin_addr),
                                        ntohs(clientaddr.sin_port));
    return peerfd;
}
int getchannelsevent(channel_t channels[],int max,int fd){
    for(int i = 0;i < max;i++){
        if(channels[i].fd==fd){
            return i;
        }
    }
    return -1;
}
void delepollfd(int epfd,int fd){
    struct epoll_event evt;
    evt.data.fd=fd;
    epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&evt);
}
void clearrecvbuf(channel_t *channel){
    memset(channel->recvbuf,0,BUFSIZE);
    channel->recvbufsize=0;
}
void clearsendbuf(channel_t *channel){
    memset(channel->sendbuf,0,BUFSIZE);
    channel->sendbufsize=0;
}
void delchannel(channel_t channels[],int max,int fd){
    for(int i = 0;i<max;i++){
        if(channels[i].fd==fd){
            channels[i].fd = -1;
            clearrecvbuf(&channels[i]);
            clearsendbuf(&channels[i]);
        }
    }
}
void setnonblock(int fd){
    int flags = fcntl(fd,F_GETFL,0);
    flags |= O_NONBLOCK;
    fcntl(fd,F_GETFL,flags);

}

void putsFunction(int clientfd,char *pfilenamebuf,int epollfd,MYSQL * conn, char *username){
    //撤掉clientfd监听
    struct epoll_event events;
    events.data.fd = epollfd;
    events.events = EPOLLOUT;
    epoll_ctl(epollfd,EPOLL_CTL_MOD,clientfd,&events);

    int ret;
    int filefd = open(pfilenamebuf,O_RDONLY);
    off_t filexistmark = 0; //用于记录文件当前位置，及偏移量
    if(0 == filefd){
        //说明没有该文件
        filefd = open(pfilenamebuf,O_RDWR | O_CREAT, 0666);
        filexistmark = 0;
    }else{
        //说明该文件已经存在
        struct stat file_stat;
        fstat(filefd,&file_stat);
        filexistmark = file_stat.st_size;
    }

    //接受文件总大小
    int filesizeoflength = 0;
    long filesize = 0;
    ret = recv(clientfd,&filesizeoflength,4,MSG_WAITALL);
    printf("recv %d charactor\n",ret);
    ret = recv(clientfd,&filesize,filesizeoflength,MSG_WAITALL);
    printf("recv %d charactor\n",ret);
    printf(">>filesize length:%ld<<\n",filesize);

    //发送偏移量
    ret = send(clientfd,&filexistmark,4,0);
    printf("send %d charactor\n",ret);
    printf("send file offset:%ld\n",filexistmark);

    //判断大文件小文件
    if(filesize < (long)100* 1024){
        printf("small file\n");
        lseek(filefd,SEEK_SET,filexistmark);
        putsFunction_small(clientfd,filefd,filesize);
    }else{
        printf("big file\n");
        ftruncate(filefd,filexistmark);
        //接受文件
        char * pbuf = (char *) mmap(NULL,filesize,PROT_READ | PROT_WRITE, MAP_SHARED, filefd, 0);
    
        recv(clientfd,pbuf+filexistmark,filesize - filexistmark,MSG_WAITALL);
        printf("recv big file over\n");
        close(filefd);
        munmap(pbuf,filexistmark );
    }

    //撤掉clientfd监听
    events.data.fd = epollfd;
    events.events = EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_MOD,clientfd,&events);
}

int sendn(int fd, const void * buf, int len)                                  
{
    int left = len;
    char * pbuf = (char *)buf;
    int ret;
    while(left > 0) {
        ret = send(fd, pbuf, left, 0);
        if(ret < 0) {
            perror("send");
            break;
        } else {
            pbuf += ret;
            left -= ret;
        }
    }
    return len - left;
}

void getsFunction(int clientfd, char * filename){
    int ret;
    train_t train;
   
    int filefd = open(filename, O_RDWR);
    ERROR_CHECK(filefd,-1,"open file");
    //发送长度
    memset(&train,0,sizeof(train));
    struct stat file_stat;
    fstat(filefd,&file_stat);
    train.length = sizeof(off_t);
    strncpy(train.data,(char*)&file_stat.st_size,train.length);
    ret = sendn(clientfd,&train,4 + train.length);
    printf("file length:%ld\n",file_stat.st_size);

    int filexistmark = 0;
    ret = recv(clientfd,&filexistmark,4,0);
    printf("recv file offset:%d\n",filexistmark);
    if(file_stat.st_size < (long)100*1024){
        printf("small file\n");
        lseek(filefd,SEEK_SET,filexistmark);
        getsFunction_small(clientfd,filename);
    }else{
    }
}

void putsFunction_bigfile(int clientfd,int filefd,int filesize,int filexistmark)
{
    int ret = 0;
        printf("big file\n");
        //发送文件内容
        char * pbuf = (char *)mmap(NULL,
                filesize,PROT_READ | PROT_WRITE,MAP_SHARED, filefd, 0);
        printf("nihao\n");

        ret = sendn(clientfd,pbuf+filexistmark,filesize - filexistmark);
        printf("big file send over!\n");
        close(filefd);
        munmap(pbuf, filesize);

}

void putsFunction_small(int clientfd,int filefd,int filesize){
    send(clientfd,"server is already getsfile",27,0);
    int retval;
    off_t slice = filesize/100;
    off_t downloadsize=0;
    off_t lastsize = 0;
    char buf[1024] = {0};
    while(downloadsize<filesize){
        memset(buf,0,sizeof(buf));
        retval = recv(clientfd,buf,sizeof(buf),0);
        downloadsize+=retval;
        if(downloadsize - lastsize>slice){
            printf(">> download percent:%5.2lf%%\n",(double)downloadsize/filesize*100);
            lastsize = downloadsize;
        }
        retval = write(filefd,buf,retval);
        ERROR_CHECK(retval,-1,"write");
    }
    printf(">>download percent:100%%\n");
    printf("putsfunc is over!\n");
    close(filefd);
}
void getsFunction_small(int clientfd, char* filename){
    train_t train;
    memset(&train,0,sizeof(train));
    printf("begin!\n");
    char buf[1024]={0};
    memset(buf,0,sizeof(buf));
    int fd = open(filename,O_RDONLY);
    struct stat st;
    fstat(fd,&st);
    printf(">> file length:%ld\n",st.st_size);
    train.length=st.st_size;
    send(clientfd,&train,4,0);
    printf("send:%d\n",train.length);
   // int length = st.st_size;
   // sprintf(buf,"%d",length);
   // printf("buf:%s\n",buf);
   // send(clientfd,buf,strlen(buf),0);
   // printf("send:%s\n",buf);
    off_t sendsize = 0;
    //memset(buf,0,sizeof(buf));
    while(sendsize<st.st_size){
        int retval = read(fd,buf,sizeof(buf));
        retval = send(clientfd,buf,retval,0);
        sendsize+=retval;
        memset(buf,0,sizeof(buf));
    }
    close(fd);
    printf("file send over!\n");
}
int examinePasswd(int clientfd){
    char account[1024]={0};
    char passwd[1024]={0};
    char buf[1024]={0};
    memset(account,0,sizeof(account));
    memset(passwd,0,sizeof(passwd));
    memset(buf,0,sizeof(buf));
    int u = 0;
    int x = 0;
    int z = 0;
    int t = 0;
    int len = recv(clientfd,buf,sizeof(buf),0);
    printf("send:%s\n",buf);
    for(;z<len;z++){//寻找命令以及文件名
        while(buf[z]!=32&&z<len-1){//32为ASCII码中空值，寻
            account[u]=buf[z];//组成命令
            z++;
            u++;
            if(buf[z]==32||z==len-1){
                t = z;
                z =len;
            }
        }
        printf("账号为：%s\n",account);
    }//文件名和命令名的查找到此为止。
    for(;t<len;t++){//有文件名的话 肯定在命令后面 接着上面
        while(buf[t]!=32&&t<len-1){
            passwd[x]=buf[t];//组成文件名
            x++;
            t++;
            if(buf[t]==32||t==len-1){//如果等于空或者到头>
                t = len;
            }
        }
    }
    printf("密码为：%s\n",passwd);


    ////////////////////////////////
      struct spwd * spwd  = NULL;
    char * pass_name    = NULL;
    char * crypt_pass   = NULL;

    pass_name=passwd;
    spwd=getspnam(account);


    crypt_pass=crypt(pass_name,spwd->sp_pwdp);
    if(crypt_pass==NULL)
    {
        fprintf(stderr,"crypt error\n");
    }


      if(strcmp(crypt_pass, spwd->sp_pwdp) == 0)
    {
        char buff[1024] = "login success!";
        send(clientfd,buff,strlen(buff),0);
        memset(buff,0,sizeof(buff));
        return 1;
    }
     else
     {
        char buff[1024] = "login failed!";
        send(clientfd,buff,strlen(buff),0);
        memset(buff,0,sizeof(buff));
        close(clientfd);
        puts("error!");
        return 0;
     }

}
void pthreadpollinit(pthreadpoll_t pthreadpoll[]){
    for(int i = 0;i<MAXEVENTS;i++){
        pthread_t pid;
        pthreadpoll[i].flag=0;
        pthreadpoll[i].clientfd = -1;
        pthread_create(&pid,NULL,waitfunc,&pthreadpoll[i]);
    }
}
void* waitfunc(void* arg){
    pthreadpoll_t* p = (pthreadpoll_t*)arg;
    printf("thread: pid = %d,tid = %lu\n",getpid(),pthread_self());
    while(1){
        if(p->flag==1){
           // char filename[19] = "The_Holy_Bible.txt";
            printf("doing work pid:%d,tid:%lu\n",getpid(),pthread_self());
           // getsFunction(p->clientfd,p->filename);
            //根据命令进入不同线程功能
                    if( p->order[0] == 'c' && p->order[1] == 'd'){
                    }else if(p-> order[0] == 'l' && p->order[1] == 's'){
                    }else if(p->order[0] == 'p' && p->order[1] == 'w' && p->order[2] == 'd'){
                    }else if(p->order[0] == 'p' && p->order[1] == 'u' && p->order[2] == 't' && p->order[3] == 's'){
                        printf("execute putsFunction\n");
                        putsFunction(p->clientfd,p->filename,p->epfd);
                    }else if(p->order[0] == 'g' && p->order[1] == 'e' && p->order[2] == 't' && p->order[3] == 's'){
                        printf("execute getsFunction\n");
                        getsFunction(p->clientfd,p->filename);
                    }else if(p->order[0] == 'r' && p->order[1] == 'm'){
                    }else if(p->order[0] == 'm' && p->order[1] == 'k' && p->order[2] == 'd' && p->order[3] == 'i' && p->order[4] == 'r'){
                    }else if(p->order[0] == 'e' && p->order[1] == 'x' && p->order[2] == 'i' && p->order[3] == 't'){
                    }else{
                        printf("this order is illegal.\n");
                    }
            p->flag=0;
            p->clientfd=-1;
            printf("now pid:%d,tid%lu is end work\n",getpid(),pthread_self());

        }
    }
}
void dowork(pthreadpoll_t pthreadpoll[],int clientfd,char* filename,char* order,int epfd){
    for(int i = 0;i<MAXEVENTS;i++){
        if(pthreadpoll[i].flag==0){
            pthreadpoll[i].flag=1;
            pthreadpoll[i].clientfd=clientfd;
            pthreadpoll[i].filename=filename;
            pthreadpoll[i].order=order;
            pthreadpoll[i].epfd=epfd;
            return;
        }
    }
}
